﻿using Hicap.QRCodeWarehouse.Processor.Model;
using Microsoft.EntityFrameworkCore;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Hicap.QRCodeWarehouse.Processor.Manager
{
    /// <summary>
    /// Monitor repository
    /// </summary>
    public sealed class MonitorManager : ManagerBase, IMonitorManager
    {
        private readonly IMongoCollection<WarhouseMonitor> _collection;
        private readonly ICodeTakenManager _codeTakenManager;

        public MonitorManager(IMongoClient mongoClient, ICodeTakenManager codeTakenManager) : base(mongoClient)
        {
            _codeTakenManager = codeTakenManager;
            _collection = GetCollection<WarhouseMonitor>("WarhouseMonitor");
        }

        /// <summary>
        /// Get monitor by id
        /// </summary>
        /// <param name="productId">int</param>
        /// <returns>WarhouseMonitor</returns>
        public async Task<WarhouseMonitor> GetWarhouseMonitorAsync(int productId)
        {
            return await _collection.AsQueryable().FirstOrDefaultAsync(x => x.ProductId == productId);
        }

        /// <summary>
        /// Get all monitor feedback(concurrence)
        /// </summary>
        /// <returns>MonitorFeedback[]</returns>
        public async Task<MonitorFeedback[]> GetMonitorInfos(int type)
        {
            List<Task<MonitorFeedback>> tasks = new List<Task<MonitorFeedback>>();
            foreach (WarhouseMonitor warhouseMonitor in GetAllWarhouseMonitor())
            {
                if (type == 1) warhouseMonitor.AuditLog.Clear();
                tasks.Add(GetMonitorInfo(warhouseMonitor, type));
            }

            return await Task.WhenAll(tasks);
        }

        /// <summary>
        /// Get all monitor(queryable)
        /// </summary>
        /// <returns></returns>
        private IEnumerable<WarhouseMonitor> GetAllWarhouseMonitor()
        {
            return _collection.AsQueryable().AsEnumerable();
        }

        /// <summary>
        /// Get monitor feedback from monitor(concurrence)
        /// </summary>
        /// <param name="overview">WarhouseMonitor</param>
        /// <param name="type">int</param>
        /// <returns></returns>
        private async Task<MonitorFeedback> GetMonitorInfo(WarhouseMonitor overview, int type)
        {
            //get realtime info tasks
            List<Task<RealTimeWarehouseMonitor>> tasks = new List<Task<RealTimeWarehouseMonitor>>()
            {
                GetRealTimeWarehouseMonitor(overview.ProductId, overview.ProductCode, 0,overview.SubordinateCompanyId),
                GetRealTimeWarehouseMonitor(overview.ProductId, overview.ProductCode, 1,overview.SubordinateCompanyId)
            };

            //concurrence load
            RealTimeWarehouseMonitor[] realtime = await Task.WhenAll(tasks);

            return new MonitorFeedback
            {
                MonitorActionType = type,
                Overview = overview,
                InnerRealTime = realtime[0],
                OutterRealTime = realtime[1]
            };
        }

        /// <summary>
        /// Get realtime info
        /// </summary>
        /// <param name="productId">int</param>
        /// <param name="productCode">string</param>
        /// <param name="generationRuleType">int</param>
        /// <param name="subordinateCompanyId">subordinateCompanyId</param>
        /// <returns>RealTimeWarehouseMonitor</returns>
        private async Task<RealTimeWarehouseMonitor> GetRealTimeWarehouseMonitor(int productId, string productCode,
            int generationRuleType, int subordinateCompanyId)
        {
            //partial result
            RealTimeWarehouseMonitor result = new RealTimeWarehouseMonitor()
            {
                ProductId = productId,
                ProductCode = productCode,
                GenerationRuleType = generationRuleType,
                SubordinateCompanyId = subordinateCompanyId
            };

            //get collection
            IMongoCollection<QrCodeRecord> collection = _codeTakenManager.GetCollection<QrCodeRecord>($"{productCode}_{generationRuleType}");

            //get count tasks
            List<Task<long>> tasks = new List<Task<long>>
            {
                collection.CountDocumentsAsync(x => !string.IsNullOrEmpty(x.Code)),
                collection.CountDocumentsAsync(x => x.IsDownloaded),
                collection.CountDocumentsAsync(x => x.IsUsed)
            };

            //concurrence load
            long[] countArray = await Task.WhenAll(tasks);

            //return count
            result.TotalCount = Convert.ToInt32(countArray[0]);
            result.DownloadedCount = Convert.ToInt32(countArray[1]);
            result.UsedCount = Convert.ToInt32(countArray[2]);

            return result;
        }

        /// <summary>
        /// Get warhouse monitor min taskId
        /// </summary>
        /// <returns>int</returns>
        /// <remarks>
        ///<para>When importing, the data needs to be mounted under the specific task, so you need to get the minimum task id and subtract one</para>
        /// </remarks>
        public int GetWarhouseMonitorMinTaskId()
        {
            int[] array = _collection.AsQueryable().Select(x => x.AuditLog.Select(y => y.TaskId).Min()).Distinct().ToArray();
            return array.Min();
        }

        /// <summary>
        /// Upsert monitor info
        /// </summary>
        /// <param name="monitor">WarhouseMonitor</param>
        /// <param name="clientSession">IClientSessionHandle</param>
        /// <returns></returns>
        public async Task UpsertAsync(WarhouseMonitor monitor, IClientSessionHandle clientSession)
        {
            WarhouseMonitor result = await _collection.FindOneAndReplaceAsync(x => x.ProductId == monitor.ProductId, monitor);
            if (result == null)
            {
                await _collection.InsertOneAsync(clientSession, monitor);
            }

            Log.Information($"Monitor Upsert:{monitor.ToJson()}");
        }
    }
}